package edu.arizona.sista.learn.language;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import com.martiansoftware.jsap.FlaggedOption;
import com.martiansoftware.jsap.JSAP;
import com.martiansoftware.jsap.JSAPResult;
import com.martiansoftware.jsap.Switch;

import edu.arizona.sista.learn.activity.experiment.RecognitionExperiment;
import edu.arizona.sista.learn.activity.fluents.paths.PredicateExtractor;
import edu.arizona.sista.learn.activity.fluents.paths.PredicateExtractor2;
import edu.arizona.sista.learn.activity.fluents.paths.SceneEntity;
import edu.arizona.sista.learn.activity.model.ActivityInstance;
import edu.arizona.sista.learn.activity.model.Predicate;
import edu.arizona.sista.learn.activity.model.PredicateInterval;
import edu.arizona.sista.learn.activity.model.args.PredAgent;
import edu.arizona.sista.learn.activity.model.args.PredArg;
import edu.arizona.sista.learn.activity.model.args.PredObject;
import edu.arizona.sista.learn.model.Interval;
import edu.arizona.sista.learn.model.ParamInterval;
import edu.arizona.sista.learn.model.displaying.DisplayingSomething;
import edu.arizona.sista.learn.model.displaying.WatchingSomething;
import edu.arizona.sista.learn.model.groups.BodyPart;
import edu.arizona.sista.learn.model.groups.Group;
import edu.arizona.sista.learn.model.groups.GroupDefinitions;
import edu.arizona.sista.learn.model.groups.GroupFormation;
import edu.arizona.sista.learn.model.groups.Person;
import edu.arizona.sista.learn.model.groups.Scene;
import edu.arizona.sista.learn.model.groups.Thing;
import edu.arizona.sista.learn.model.holding.HoldingAnObject;
import edu.arizona.sista.learn.model.interest.Walking;
import edu.arizona.sista.learn.model.interest.WalkingBackwards;
import edu.arizona.sista.learn.model.interest.WalkingForwards;
import edu.arizona.sista.learn.util.Utils;
import edu.arizona.sista.learn.util.Utils.MovieName;

public class FluentsExtraction {
	
	private static String outDir = "data/" + Utils.OUTDIR + "fluents/";

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		JSAP jsap = new JSAP();
		Switch helpSwitch = new Switch("help")
				.setShortFlag('h')
				.setLongFlag("help");
		FlaggedOption movie = new FlaggedOption("movie")
				.setDefault("")
				.setRequired(true)
				.setLongFlag("movie");
		FlaggedOption tracks = new FlaggedOption("3dtracks")
				.setDefault("")
				.setRequired(false)
				.setLongFlag("3dtracks");
		FlaggedOption initGrpConfig = new FlaggedOption("initgroup")
				.setDefault("")
				.setRequired(false)
				.setLongFlag("initgroup");
		FlaggedOption look = new FlaggedOption("look")
				.setDefault("")
				.setRequired(false)
				.setLongFlag("look");
		FlaggedOption pairs = new FlaggedOption("pairs")
				.setDefault("")
				.setRequired(false)
				.setLongFlag("pairs");
		FlaggedOption groups = new FlaggedOption("groups")
				.setDefault("")
				.setRequired(false)
				.setLongFlag("groups");
		FlaggedOption gesture = new FlaggedOption("gestures")
				.setDefault("")
				.setRequired(false)
				.setLongFlag("gestures");
		FlaggedOption holding = new FlaggedOption("holding")
				.setDefault("")
				.setRequired(false)
				.setLongFlag("holding");
		FlaggedOption walking = new FlaggedOption("walking")
				.setDefault("")
				.setRequired(false)
				.setLongFlag("walking");
		FlaggedOption displaying = new FlaggedOption("displaying")
				.setDefault("")
				.setRequired(false)
				.setLongFlag("displaying");
		FlaggedOption out = new FlaggedOption("out")
				.setDefault("")
				.setRequired(false)
				.setLongFlag("out");
		jsap.registerParameter(helpSwitch);
		jsap.registerParameter(movie);
		jsap.registerParameter(tracks);
		jsap.registerParameter(initGrpConfig);
		jsap.registerParameter(look);
		jsap.registerParameter(pairs);
		jsap.registerParameter(groups);
		jsap.registerParameter(gesture);
		jsap.registerParameter(holding);
		jsap.registerParameter(walking);
		jsap.registerParameter(displaying);
		jsap.registerParameter(out);

		JSAPResult config = jsap.parse(args);
		if (config.getBoolean("help") || "".equals(config.getString("movie"))) {
			System.out.print("java -jar fluents-extraction.jar ");
			System.out.println(jsap.getUsage());
			return;
		}
		
		// Get values from command args
		String movieName = config.getString("movie");
		String tracksDir = config.getString("3dtracks");
		String lookDir = config.getString("look");
		String groupsDir = config.getString("groups");
		if (!groupsDir.endsWith("/")) groupsDir += "/";
		String pairsFile = config.getString("pairs");
		String initGrpConfigFile = config.getString("initgroup");
		String gesturesDir = config.getString("gestures");
		String holdingDir = config.getString("holding");
		String walkingGazeDir = config.getString("walking");
		String displayingDir = config.getString("displaying");
		outDir = ("".equals(config.getString("out"))) ? outDir : config.getString("out");
		if (!outDir.endsWith("/")) outDir += "/";
		int tracksFormat = 3;
		
		
		// Hard-coded values for fast debug
//		movieName = "CR1_00002";
//		tracksFormat = 3;
//		String dataRoot = "ground-truth-Y2";
//		initGrpConfigFile = "/Users/trananh/Desktop/psi-y2/"+dataRoot+"/"+movieName+"/groups/"+movieName+"-stats-group.csv";
//		lookDir = "/Users/trananh/Desktop/psi-y2/"+dataRoot+"/"+movieName+"/look-at";
//		gesturesDir = "/Users/trananh/Desktop/psi-y2/"+dataRoot+"/"+movieName+"/gestures/binary";
//		tracksDir = "/Users/trananh/Desktop/psi-y2/"+dataRoot+"/"+movieName+"/3d-tracks";
//		pairsFile = "/Users/trananh/Desktop/psi-y2/"+dataRoot+"/"+movieName+"/groups/"+movieName+"-pairs.csv";
//		groupsDir = "/Users/trananh/Desktop/psi-y2/"+dataRoot+"/"+movieName+"/groups/";
//		outDir = "/Users/trananh/Desktop/psi-y2/"+dataRoot+"/"+movieName+"/fluents/";
//		
//		movieName = "newspaper"; // map || newspaper
//		tracksFormat = 2;
//		initGrpConfigFile = "/Users/trananh/Desktop/psi-y2/ground-truth/"+movieName+"/3d-clusters/"+movieName+".csv";
//		lookDir = "/Users/trananh/Desktop/psi-y2/ground-truth/"+movieName+"/look-at";
//		tracksDir = "/Users/trananh/Desktop/psi-y2/ground-truth/"+movieName+"/3d-tracks";
//		groupsDir = "/Users/trananh/Desktop/psi-y2/ground-truth/"+movieName+"/groups";
//		pairsFile = "/Users/trananh/Desktop/psi-y2/ground-truth/"+movieName+"/pairs/"+movieName+".csv";
//		holdingDir = "/Users/trananh/Workspace/Java/ua-social-groups-modeling/data/holding/"+movieName;
//		walkingGazeDir = "/Users/trananh/Workspace/Java/ua-social-groups-modeling/data/gaze/"+movieName;
//		displayingDir = "/Users/trananh/Workspace/Java/ua-social-groups-modeling/data/displaying/"+movieName;
		
		
		MovieName m = MovieName.fromString(movieName);

		List <PredicateInterval> lookAts = new ArrayList<PredicateInterval>();
		if (!lookDir.equals("")) {
			// Look-at data
			lookAts = getLookAtFluents(m, new File(lookDir));
			ActivityInstance inst = new ActivityInstance(movieName, lookAts);
			inst.toFile(new File(outDir +
					movieName + "-fluents-lookat.lisp"), false);
		}
		
		List <PredicateInterval> gestures = new ArrayList<PredicateInterval>();
		if (!gesturesDir.equals("")) {
			// Gestures data
			gestures = getGesturesFluents(m, new File(gesturesDir));
			ActivityInstance inst = new ActivityInstance(movieName, gestures);
			inst.toFile(new File(outDir +
					movieName + "-fluents-gestures.lisp"), false);
			
			// Remove walk & run since we're already getting these
			// from activities recognition.
			for (int i = gestures.size()-1; i >= 0; i--) {
				PredicateInterval pi = gestures.get(i);
				if (pi.getPredicate().getName().equalsIgnoreCase("walk") ||
						pi.getPredicate().getName().equalsIgnoreCase("run")) {
					gestures.remove(i);
				}
					
			}
		}
		
		List <PredicateInterval> pairsTogether = new ArrayList<PredicateInterval>();
		if (!pairsFile.equals("")) {
			pairsTogether = getPairsFluents(m, new File(pairsFile), new int[]{ 75, 90 });
			ActivityInstance inst = new ActivityInstance(movieName, pairsTogether);
			inst.toFile(new File(outDir +
					movieName + "-fluents-pairs.lisp"), false);
		}
		
		List <PredicateInterval> initGroups = new ArrayList<PredicateInterval>();
		if (!initGrpConfigFile.equals("") && !groupsDir.equals("")) {
			// Initial grouping configurations
			initGroups = getInitialGroupingFluents(m, new File(initGrpConfigFile),
					groupsDir + movieName + "-init-group.txt");
			ActivityInstance inst = new ActivityInstance(movieName, initGroups);
			inst.toFile(new File(outDir +
					movieName + "-fluents-init.lisp"), false);
		}

		List <PredicateInterval> sptGroups = new ArrayList<PredicateInterval>();
		if (!initGrpConfigFile.equals("") && !groupsDir.equals("")) {
			// Spatio-temporal grouping
			sptGroups = getSpatioTemporalGroupingFluents(m, new File(initGrpConfigFile),
					groupsDir + movieName + "-spt-group.txt");
			ActivityInstance inst = new ActivityInstance(movieName, sptGroups);
			inst.toFile(new File(outDir +
					movieName + "-fluents-spt.lisp"), false);
		}

		List <PredicateInterval> socialGroups = new ArrayList<PredicateInterval>();
		if (!initGrpConfigFile.equals("") && !lookDir.equals("") && !groupsDir.equals("")) {
			// Social grouping
			socialGroups = getSocialGroupingFluents(m,
					new File(initGrpConfigFile), new File(lookDir),
					groupsDir + movieName + "-social-group.txt");
			ActivityInstance inst = new ActivityInstance(movieName, socialGroups);
			inst.toFile(new File(outDir +
					movieName + "-fluents-social.lisp"), false);
		}

		List <PredicateInterval> rulesActivities = new ArrayList<PredicateInterval>();
		if (!holdingDir.equals("")) {
			rulesActivities.addAll(getHoldingRuleFluents(m,
					new File(holdingDir + "/pose-all.csv"),
					new File(holdingDir + "/body-parts.csv")));
			if (!walkingGazeDir.equals("")) {
				// Activities from rules
				rulesActivities.addAll(getWalkingRuleFluents(m,
						new File(holdingDir + "/pose-all.csv"),
						new File(holdingDir + "/body-parts.csv"),
						new File(walkingGazeDir)));
			}
			if (!displayingDir.equals("")) {
				// Activities from rules
				rulesActivities.addAll(getDisplayingRuleFluents(m,
						new File(holdingDir + "/pose-all.csv"),
						new File(holdingDir + "/body-parts.csv"),
						new File(displayingDir + "/lookat.csv")));
			}
			
			ActivityInstance inst = new ActivityInstance(movieName, rulesActivities);
			inst.toFile(new File(outDir +
					movieName + "-fluents-rules.lisp"), false);
		}
		
		List <PredicateInterval> verbs = new ArrayList<PredicateInterval>();
		if (!tracksDir.equals("")) {
			// Translational verbs
			verbs = getActivityFluents(m, tracksDir, tracksFormat);
			ActivityInstance inst = new ActivityInstance(movieName, verbs);
			inst.toFile(new File(outDir +
					movieName + "-fluents-activities.lisp"), false);
		}
		
		// Combine all
		ActivityInstance inst = new ActivityInstance(movieName, new ArrayList<PredicateInterval>());
		inst.addIntervals(lookAts);
		inst.addIntervals(gestures);
//		inst.addIntervals(pairsTogether);
		inst.addIntervals(sptGroups);
		inst.addIntervals(socialGroups);
		inst.addIntervals(verbs);
		inst.addIntervals(rulesActivities);
		inst.toFile(new File(outDir +
				movieName + "-fluents.lisp"), false);
	}
	

	private static List<PredicateInterval> getPairsFluents(MovieName m, File file,
			int[] thresholds) {
		List<PredicateInterval> pairsFluents = new ArrayList<PredicateInterval>();
		
		Scene scene = new Scene(m);
		scene.loadPairsData(file);
		Map<Set<Person>, List<Double>> pairs = scene.getPairs();
		
		// For each pair, we'll threshold their probability series
		// to get intervals.
		for (Set<Person> pair : pairs.keySet()) {
			// Note: pairs may now have any arbitrary number of members
			Person[] pairArray = pair.toArray(new Person[0]);
			List<PredArg> predArgs = new ArrayList<PredArg>();
			for (Person p : pairArray) {
				predArgs.add(new PredAgent(null, p.name()));
			}
			List<Double> probs = pairs.get(pair);
			
			// We'll try several threshold values, each of which will
			// be an argument to the predicate.
			for (int threshold : thresholds) {
				List<PredArg> newArgs = new ArrayList<PredArg>(predArgs);	// shallow copy
				newArgs.add(0, new PredObject(null, ""+threshold));
				Predicate pPred = new Predicate("pair", newArgs);
				List<Interval> intervals = Utils.thresholdSeriesToIntervals(probs, threshold/100.0);
				for (Interval i : intervals) {
					pairsFluents.add(new PredicateInterval(pPred, i.start(), i.end()));
				}
			}
		}
		
		return pairsFluents;
	}


	private static List<PredicateInterval> getHoldingRuleFluents(
			MovieName m, File poseFile, File bodyPartsFile) {
		List<PredicateInterval> activities = new ArrayList<PredicateInterval>();
		
		// Load data
		edu.arizona.sista.learn.model.holding.Scene scene =
			new edu.arizona.sista.learn.model.holding.Scene(m);
		scene.loadPositionData(poseFile);
		scene.loadBodyParts(bodyPartsFile);

		List<HoldingAnObject> holds = HoldingAnObject.getHoldThings(scene);
		for (HoldingAnObject h : holds) {
			PredArg doer = new PredAgent(null, h._doer.getVariableName());
			PredArg hand = new PredObject(null, h._doer.getVariableName()+"."+h._hand.getLabel());
			PredArg obj = new PredObject(null, h._object.getVariableName().replace("_", ""));
			Predicate holding = new Predicate("holding", doer, hand, obj);
			activities.add(new PredicateInterval(holding, h._time.start(),
					h._time.end()));
		}
		
		return activities;
	}
	
	private static List<PredicateInterval> getWalkingRuleFluents(
			MovieName m, File poseFile, File bodyPartsFile, File gazeFolder) {
		List<PredicateInterval> activities = new ArrayList<PredicateInterval>();
		
		// Load data
		edu.arizona.sista.learn.model.holding.Scene scene =
			new edu.arizona.sista.learn.model.holding.Scene(m);
		scene.loadPositionData(poseFile);
		scene.loadBodyParts(bodyPartsFile);

		Walking.computeWalkingEvents(scene, gazeFolder);
		List<WalkingBackwards> wb = scene.getWalkingBackwardsEvents();
		for (WalkingBackwards w : wb) {
			PredArg doer = new PredAgent(null, w._doer.getVariableName());
			Predicate walkingBackwards = new Predicate("walking-backwards", doer);
			activities.add(new PredicateInterval(walkingBackwards, w._time.start(),
					w._time.end()));
		}
		List<WalkingForwards> wf = scene.getWalkingForwardsEvents();
		for (WalkingForwards w : wf) {
			PredArg doer = new PredAgent(null, w._doer.getVariableName());
			Predicate walkingForwards = new Predicate("walking-forwards", doer);
			activities.add(new PredicateInterval(walkingForwards, w._time.start(),
					w._time.end()));
		}
		
		return activities;
	}
	
	private static List<PredicateInterval> getDisplayingRuleFluents(
			MovieName m, File poseFile, File bodyPartsFile, File lookFile) {
		List<PredicateInterval> activities = new ArrayList<PredicateInterval>();

		// Load data
		edu.arizona.sista.learn.model.holding.Scene scene =
			new edu.arizona.sista.learn.model.holding.Scene(m);
		scene.loadPositionData(poseFile);
		scene.loadBodyParts(bodyPartsFile);
		
		//Get hold things
		HoldingAnObject.getHoldThings(scene);
		
		//Get watched things
		WatchingSomething.loadLook(lookFile, scene);
		
		//Get displayed things
		DisplayingSomething.getDisplayedThings(scene, Interval.NOISE_WINDOW);
		List<DisplayingSomething> ds = scene.getDisplayingSomethingEvents();
		for (DisplayingSomething d : ds) {
			PredArg doer = new PredAgent(null, d._doer.getVariableName());
			PredArg obj = new PredObject(null, d._object.getVariableName().replace("_", ""));
			Predicate displaying = new Predicate("displaying", doer, obj);
			activities.add(new PredicateInterval(displaying, d._time.start(),
					d._time.end()));
		}
		
		return activities;
	}


	private static List<PredicateInterval> getActivityFluents(MovieName m,
			String tracksDir, int tracksFormat) {
		List<PredicateInterval> verbs = new ArrayList<PredicateInterval>();
		try {
			// Load scene
			edu.arizona.sista.learn.activity.fluents.paths.Scene scene
				= edu.arizona.sista.learn.activity.fluents.paths.Scene.load(tracksDir, tracksFormat);
			
			if (scene != null) {
				// Let's switch up the names of these entities if we have more descriptive names
				if (m != null) {
					Vector<SceneEntity> entities = scene.getEntities();
					for (SceneEntity entity : entities) {
						if (entity.getType().equalsIgnoreCase("person")) {
							entity.setName(Utils.getActorNameFromTrackID(m, ""+entity.getId()).toString());
						}
					}
				}
	
				// Extract fluents and generate activity instance
				PredicateExtractor2 extractor = new PredicateExtractor2(scene, PredicateExtractor.DataType.Points);
				Vector<Vector<Predicate>> preds = extractor.extractPredicates();
				ActivityInstance instance = extractor.getPredicateIntervals(preds, false);
				System.out.println("\n" + instance.toString() + "\n");
				
				// Run recognizers
				Map<String, List<PredicateInterval>> results = RecognitionExperiment.recognition(instance, false);
				for (String key : results.keySet()) {
					verbs.addAll(results.get(key));
				}
				
				// The intervals returned have the form [start, stop], we want to turn
				// them into [start stop).
				for (PredicateInterval pi : verbs) {
					pi.end += 1;
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		return verbs;
	}
	

	private static List<PredicateInterval> getLookAtFluents(MovieName m, File look) {
		Scene scene = new Scene(m);
		scene.loadGazeData(look, false);
		
		List<PredicateInterval> looks = new ArrayList<PredicateInterval>();
		for (Person source : scene.getActors()) {
			PredArg s = new PredAgent(null, source.name());
			Collections.sort(source.lookAt());
			for (ParamInterval<Thing> in : source.lookAt()) {
				Thing target = in.obj();
				PredArg t = null;
				if (target instanceof Person)
					t = new PredAgent(null, target.name());
				else if (target instanceof BodyPart) {
					t = new PredObject(null,
							((BodyPart)target).owner().name()
							+ "." + target.name());
				} else {
					t = new PredObject(null, target.name());
				}
				Predicate pred = new Predicate("look-at", s, t);
				PredicateInterval pi = new PredicateInterval(pred,
						in.interval().start(), in.interval().end());
				looks.add(pi);
			}
		}
		
		return looks;
	}
	
	
	private static List<PredicateInterval> getGesturesFluents(MovieName m, File gestFile) {
		Scene scene = new Scene(m);
		scene.loadGesturesData(gestFile, false);
		
		List<PredicateInterval> gestures = new ArrayList<PredicateInterval>();
		for (Person actor : scene.getActors()) {
			PredArg a = new PredAgent(null, actor.name());
			Collections.sort(actor.gestures());
			for (ParamInterval<String> in : actor.gestures()) {
				String verb = in.obj();
				Predicate pred = new Predicate(verb, a);
				PredicateInterval pi = new PredicateInterval(pred,
						in.interval().start(), in.interval().end());
				gestures.add(pi);
			}
		}
		
		return gestures;
	}
	

	private static List<PredicateInterval> getInitialGroupingFluents(
			MovieName m, File initGrpConfigFile, String outFile) {
		
		Scene scene = new Scene(m);
		scene.loadGroupConfigurations(initGrpConfigFile, false);
		
		// Output group to file
		if (!outFile.equals(""))
			Utils.toFile(scene.printGroups(), outFile);
		
		return extractGroupsFluents(scene, "init");
	}
	
	
	private static List<PredicateInterval> getSpatioTemporalGroupingFluents(
			MovieName m, File initGrpConfigFile, String outFile) {
		
		Scene scene = new Scene(m);
		scene.loadGroupConfigurations(initGrpConfigFile, false);
		
		int minParticipants = 2;
		GroupDefinitions.spatioTemporal(scene, Interval.NOISE_WINDOW, minParticipants, false);
		
		// Output group to file
		if (!outFile.equals(""))
			Utils.toFile(scene.printGroups(), outFile);
		
		return extractGroupsFluents(scene, "spt");
	}
	

	private static List<PredicateInterval> getSocialGroupingFluents(
			MovieName m, File initGrpConfigFile, File look, String outFile) {
		Scene scene = new Scene(m);
		scene.loadGroupConfigurations(initGrpConfigFile, false);
		scene.loadGazeData(look, false);
		
		int minParticipants = 2;
		float minAttentionRatio = 0.6f;
		GroupDefinitions.gazeBased(scene, minAttentionRatio, false);
		GroupDefinitions.spatioTemporal(scene, Interval.NOISE_WINDOW, minParticipants, false);

		// Output group to file
		if (!outFile.equals(""))
			Utils.toFile(scene.printGroups(), outFile);
		
		return extractGroupsFluents(scene, "social");
	}
	

	private static List<PredicateInterval> extractGroupsFluents(Scene scene,
			String groupType) {
		List<PredicateInterval> groupsFluents = new ArrayList<PredicateInterval>();
		
		for (Group g : scene.getGroups()) {
			// Group's identity predicate
			PredArg group = new PredObject(null, groupType+"-"+g.id());
			Predicate gPred = new Predicate(groupType+"-group", group);
			groupsFluents.add(new PredicateInterval(gPred,
					g.interval().start(), g.interval().end()));
			
			// Extract membership & formation changes predicates
			GroupFormation prev = null, current = null;
			int currentStart=0, currentEnd=0;
			List<ParamInterval<GroupFormation>> formations =
				new ArrayList<ParamInterval<GroupFormation>>(g.groupingVariations());
			for (int i = 0; i < formations.size(); i++) {
				current = formations.get(i).obj();
				currentStart = formations.get(i).interval().start();
				currentEnd = formations.get(i).interval().end();
				
				// Group formation predicate
				List<PredArg> members = new ArrayList<PredArg>();
				for (Person p : current.participants()) {
					members.add(new PredAgent(null, p.name()));
				}
				List<PredArg> formationArgs = members;
				formationArgs.add(0, group);
				Predicate gfPred = new Predicate(groupType+"-group-formation", formationArgs);
				groupsFluents.add(new PredicateInterval(gfPred,currentStart,currentEnd));
				
				if (prev == null) {
					prev = new GroupFormation(new HashSet<Person>());
				}
				
				// Group memberships predicates
				// Figure out who just left the group
				Set<Person> prevMembers = new HashSet<Person>(prev.participants());
				prevMembers.removeAll(current.participants());
				for (Person p : prevMembers) {
					Predicate lPred = new Predicate("membership-ends",
							group, new PredAgent(null, p.name()));
					groupsFluents.add(new PredicateInterval(lPred,currentStart-1,currentStart));
				}
				
				// Figure out who just joined the group
				Set<Person> currentMembers = new HashSet<Person>(current.participants());
				currentMembers.removeAll(prev.participants());
				for (Person p : currentMembers) {
					Predicate jPred = new Predicate("membership-starts",
							group, new PredAgent(null, p.name()));
					groupsFluents.add(new PredicateInterval(jPred,currentStart,currentStart+1));
				}
				
				prev = current;
			}
			
			// End the last group formation's membership
			for (Person p : current.participants()) {
				Predicate lPred = new Predicate("membership-ends",
						group, new PredAgent(null, p.name()));
				groupsFluents.add(new PredicateInterval(lPred,currentEnd-1,currentEnd));
			}
		}
		
		return groupsFluents;
	}

}
